package com.xframe.xdal.core.model.sqlbuilder.proc;

import com.xframe.xdal.core.constant.DalConstants;
import com.xframe.xdal.core.model.mapping.IColumnMapping;
import com.xframe.xdal.core.model.sqlbuilder.BaseSqlBuilder;
import com.xframe.xdal.core.model.sqlbuilder.BuilderContext;
import com.xframe.xdal.core.model.sqlbuilder.DbExpression;
import com.xframe.xdal.core.model.sqlbuilder.SqlExpression;
import com.xframe.xdal.core.util.XReflect;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 功能描述: OracleBatchSaveBuilder
 * 根据主键或者唯一键 比较 存在update  否则INSERT
 *
 * @version 1.0
 * @author：liangwei
 * @date: 2023/23 09:09
 **/
@Slf4j
public class OracleBatchSaveBuilder extends BaseSqlBuilder {

    /**
     * manyinsert sql语句模板
     *
     * @return
     */
    public String getSQL() {
        return "INSERT ALL %s" + " SELECT * FROM DUAL";
    }

    private static final String MergeIntoSQL = "MERGE INTO %s using (SELECT *  FROM DUAL) ON (%s) WHEN NOT matched THEN %s WHEN  matched THEN %s ;";
    private static final String UpdateSQL = "UPDATE SET %s where %s";
    private static final String InsertSQL = "INSERT (%s) VALUES (%s)";

    @Override
    public SqlExpression build(BuilderContext context) {

        List<String> fieldNameList = new ArrayList<>();
        String SQL = buildHelp(context, fieldNameList);
        //构建set
        String tableName = context.getNameConvert().toName(context.getTableInfo().getName());
        // set字段
        StringBuilder setSb = new StringBuilder();
        //主键
        StringBuilder setPrimaryKey = new StringBuilder();
        // insert字段
        StringBuilder insertStrBuilder = new StringBuilder();
        //主键名称
        String pkName = context.getNameConvert().toName(context.getTableInfo().findPkFirst().getName());
        //List<Object> param

        //主键字段名
        String pkFieldName = context.getTableInfo().findPkFirst().getFieldName();

        List<IColumnMapping> columnMappingList = context.findCanUpdateList();

        Collection collection = context.getData().getCollection();
        // 参数集合
        List<Object> paramList = new ArrayList<>();

        StringBuilder stringBuilder = new StringBuilder();
        //当前对象的参数信息
        List<Object> rowParamList = new ArrayList<>();

        collection.forEach(itemCollection -> {
            // 先清除相关数据
            setSb.setLength(0);
            setPrimaryKey.setLength(0);
            insertStrBuilder.setLength(0);
            rowParamList.clear();

            setPrimaryKey.append(pkName);
            setPrimaryKey.append(DalConstants.SQL_EQUAL);
            setPrimaryKey.append(DalConstants.SQL_PLACE_HOlDER);
            // 添加唯一键的查询条件 并用OR 连接
            List<Object> akParam = new ArrayList<>();
            StringBuilder akBuild = this.builderWhereByUniqueKey(itemCollection,context,akParam);
            setPrimaryKey.append(" OR (" + akBuild + ")");
            rowParamList.add(XReflect.getFieldValue(itemCollection, pkFieldName));
            rowParamList.addAll(akParam);

            createStrBuilder(itemCollection, SQL, insertStrBuilder, rowParamList, fieldNameList);
            columnMappingList.forEach(
                    item -> buildSetSb(item, itemCollection, setSb, context, rowParamList)
            );

            // 添加update语句WHERE部分的参数对象
            rowParamList.add(XReflect.getFieldValue(itemCollection, pkFieldName));
            rowParamList.addAll(akParam);

            String updateSql = String.format(UpdateSQL, setSb.deleteCharAt(setSb.length() - 1), setPrimaryKey);
            String realSql = String.format(MergeIntoSQL, tableName, setPrimaryKey, insertStrBuilder, updateSql);
            stringBuilder.append(realSql);
            paramList.addAll(rowParamList);
        });

        return new SqlExpression(stringBuilder.toString(), paramList, context.getDataSourceTypeEnums());

    }

    private void createStrBuilder(Object objItem, String SQL, StringBuilder
            sqlBuilder, List<Object> paramList, List<String> fieldNameList) {
        sqlBuilder.append(SQL);
        for (String fieldName : fieldNameList) {
            paramList.add(XReflect.getFieldValue(objItem, fieldName));
        }

    }


    private String buildHelp(BuilderContext context, List<String> fieldNameList) {
        List<IColumnMapping> columnMappings = context.findNotDbGenerated();

        StringBuilder colstrBuilder = new StringBuilder();
        StringBuilder valstrBuilder = new StringBuilder();

        for (int index = 0; index < columnMappings.size(); index++) {
            IColumnMapping columnMapping = columnMappings.get(index);

            colstrBuilder.append(context.getNameConvert().toName(columnMapping.getName()));
            if (columnMapping.getUseDbObjectFlag()) {
                String dbExp = DbExpression.getDbExpression().getExpression(columnMapping.getDbExpression(), context.getDataSourceTypeEnums());
                valstrBuilder.append(dbExp);
            } else {

                valstrBuilder.append(DalConstants.SQL_PLACE_HOlDER);
                fieldNameList.add(columnMapping.getFieldName());
            }
            if (index + 1 < columnMappings.size()) {
                colstrBuilder.append(DalConstants.SQL_SEPARATION);
                valstrBuilder.append(DalConstants.SQL_SEPARATION);
            }
        }
        return String.format(InsertSQL, colstrBuilder, valstrBuilder);

    }


    /**
     * 构造set字段builder
     *
     * @param columnMapping 字段
     * @param stringBuilder builder
     * @param context       上下文
     * @param paramList     参数list
     */
    protected void buildSetSb(IColumnMapping columnMapping, Object dataPara, StringBuilder stringBuilder, BuilderContext context, List<Object> paramList) {
        stringBuilder.append(columnMapping.getName());
        stringBuilder.append(DalConstants.SQL_EQUAL);
        if (columnMapping.getUseDbObjectFlag()) {
            String dbExp = DbExpression.getDbExpression().getExpression(columnMapping.getDbExpression(), context.getDataSourceTypeEnums());
            stringBuilder.append(dbExp);
        } else {
            stringBuilder.append(DalConstants.SQL_PLACE_HOlDER);
            paramList.add(XReflect.getFieldValue(dataPara, columnMapping.getFieldName()));
        }
        stringBuilder.append(DalConstants.SQL_SEPARATION);
    }
}
